using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Data;
using System.IO;

namespace MAP
{
    class MAPPER
    {
        string FOLDERPATH = MAP.Properties.Settings.Default.FolderPath;

        #region Declaration..

        List<fMap> FeildMaps = new List<fMap>();
        List<fMap> LinkTo = new List<fMap>();
        List<fMap> LinkFrom = new List<fMap>();
        List<fMap> MultiMatch = new List<fMap>();
        List<fMap> Links = new List<fMap>();
        List<fMap> Matchs = new List<fMap>();
        DataSet dsBTM = new DataSet();
        DataTable dtFunctoids = new DataTable();
        DataTable dtParameters = new DataTable();
        MasterTables MasterLoop = new MasterTables();

        #endregion

        #region Prep Table Loop functoids...

        /// <summary>
        /// Collect all Table looping data..
        /// </summary>
        private void PrepLoopTable()
        {
            DataTable TableLoopingData = dsBTM.Tables["TableLoopingData"];
            if (TableLoopingData == null) return;
            foreach (DataRow TableLoop in TableLoopingData.Rows)
            {
                string TableLoopID = TableLoop["TableLoopingData_id"].ToString().Trim();
                Table LoopingTnl = new Table();
                LoopingTnl.tableID = TableLoop["Functoid_id"].ToString().Trim();

                DataRow[] f = dsBTM.Tables["Functoid"].Select("Functoid_id='" + LoopingTnl.tableID + "'");
                if (f.Length > 0)
                {
                    LoopingTnl.FunctoidID = f[0]["FunctoidID"].ToString().Trim();
                }

                foreach (DataRow RowLoop in dsBTM.Tables["Row"].Select("TableLoopingData_id='" + TableLoopID + "'"))
                {
                    string Row_id = RowLoop["Row_id"].ToString();

                    Columns c = new Columns();
                    foreach (DataRow ColumnLoop in dsBTM.Tables["Column"].Select("Row_id='" + Row_id + "'"))
                    {
                        string GUID = ColumnLoop["Value"].ToString();
                        DataRow[] drID = dsBTM.Tables["Parameter"].Select("GUID='" + GUID + "'");
                        if (drID.Length > 0)
                        {
                            c.Column.Add(new Links(drID[0]["Value"].ToString().Trim()));
                        }
                        else
                        {
                            //for columns which are not being mapped or extracted, inserting dummy link
                            c.Column.Add(new Links("BLANK"));
                        }
                    }
                    LoopingTnl.AddColumn(c);
                }
                MasterLoop.Tables.Add(LoopingTnl);
            }
        }

        #endregion

        #region Prep Table Extractor functoids...
        /// <summary>
        /// Get all Table extractors for a given table looping
        /// </summary>
        private void prepTableExtract()
        {
            foreach (Table tbl in MasterLoop.Tables)
            {
                DataRow[] f = dsBTM.Tables["Functoid"].Select("[Functoid-FID] = 704");
                foreach (DataRow drpara in f)
                {

                    DataRow[] drParameters = dsBTM.Tables["Input-Parameters"].Select("[Functoid_ID]='" + drpara["Functoid_ID"].ToString() + "'");
                    foreach (DataRow dr in drParameters)
                    {
                        DataRow[] drp = dsBTM.Tables["Parameter"].Select("[Input-Parameters_Id]='" + dr["Input-Parameters_Id"].ToString() + "'");
                        if (drp.Length > 0)
                        {
                            DataRow[] Links = dsBTM.Tables["Link"].Select("LinkID='" + drp[0]["Value"].ToString().Trim() + "'");
                            if (Links.Length > 0)
                            {
                                if (tbl.FunctoidID == Links[0]["LinkFrom"].ToString().Trim())
                                {
                                    tbl.tblExtractors.Add(new TableExtractor(drp[1]["Value"].ToString().Trim(), drpara["FunctoidID"].ToString().Trim()));
                                }
                            }
                        }
                    }
                }
            }
        }

        #endregion

        #region ManipulateLinks
        /// <summary>
        /// ManipulateLinks to make life easy
        /// </summary>
        private void ManipulateLinks()
        {
            List<fMap> HardcodedtableLoopColumns = new List<fMap>();
            foreach (Table tbl in MasterLoop.Tables)
            {
                foreach (TableExtractor Tex in tbl.tblExtractors)
                {
                    foreach (Columns c in tbl.Rows)
                    {
                        c.Column[Convert.ToInt16(Tex.Pointer) - 1].LinkTo = Tex.FunctoidID;
                        c.Column[Convert.ToInt16(Tex.Pointer) - 1].LinkFrom = c.Column[Convert.ToInt16(Tex.Pointer) - 1].ColumnID;
                        foreach (fMap lnkto in LinkTo)
                        {
                            string LinkID = c.Column[Convert.ToInt16(Tex.Pointer) - 1].ColumnID;
                            if (lnkto.Mapped == false && lnkto.LinkID == LinkID)
                            {
                                //becuase of this methodology we will not be able to capture intermediate functoids for these links
                                lnkto.LinkTo = Tex.FunctoidID;
                                break;
                            }
                            // for hardcoded values inside table loop itself
                            else if (Regex.IsMatch(LinkID, "^[a-z]*$", RegexOptions.IgnoreCase))
                            {
                                //CREATE GHOST LINK
                                fMap f = new fMap();
                                f.LinkTo = Tex.FunctoidID;
                                f.SourceTable = "HARDCODED VALUE";
                                f.SourceColumn = LinkID;
                                HardcodedtableLoopColumns.Add(f);
                                break;
                            }
                            else
                            {   //for links which have source propagating thru various functoids before reaching Table Looping functoids
                                foreach (fMap f in Links)
                                {
                                    if (f.LinkID == LinkID)
                                    {
                                        //CREATE A JUMPER
                                        f.LinkTo = Tex.FunctoidID;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            //Adding hard coded values to LinkTo , to be compared later on
            LinkTo.AddRange(HardcodedtableLoopColumns);
        }

        #endregion

        static void Main(string[] args)
        {
            MAPPER p = new MAPPER();
            DirectoryInfo dMain = new DirectoryInfo(p.FOLDERPATH);
            p.LoopDirectories(dMain);
            Console.WriteLine("Maps generatation completed");
                    }

        private void LoopDirectories(DirectoryInfo d)
        {
            foreach (DirectoryInfo dchild in d.GetDirectories())
            {
                LoopDirectories(dchild);
            }

            foreach (FileInfo f in d.GetFiles("*.BTM"))
            {
                processFolder(f.FullName);
            }
        }

        private void processFolder(string FileName)
        {
            FeildMaps = new List<fMap>();
            LinkTo = new List<fMap>();
            LinkFrom = new List<fMap>();
            MultiMatch = new List<fMap>();
            Links = new List<fMap>();
            Matchs = new List<fMap>();
            dsBTM = new DataSet();
            dtFunctoids = new DataTable();
            dtParameters = new DataTable();
            MasterLoop = new MasterTables();

            Console.WriteLine(FileName);
            try
            {
                StartProcessing(FileName);
            }
            catch (Exception ex)
            {
                StreamWriter str = File.CreateText(Path.GetDirectoryName(FileName) + @"\ERROR.TXT");
                str.WriteLine("FAILED IN " + FileName + "\n" + ex.Message);
                str.Close();
                Console.WriteLine("FAILED IN " + FileName);
            }
        }

        private void StartProcessing(string PATH)
        {
            System.IO.StreamReader Reader = System.IO.File.OpenText(PATH);
            string XSD = Reader.ReadToEnd();
            Reader.Close();

            // TO EXCLUDE INLINE SCHEMA
            int FirstHit = XSD.IndexOf("<SrcTree");
            int LastHit = XSD.IndexOf("</TrgTree>");
            if (FirstHit != -1)
            {
                XSD = XSD.Remove(FirstHit, LastHit - FirstHit + 10);
            }
            TextReader stringReader = new StringReader(XSD);

            dsBTM.ReadXml(stringReader);
            PrepLoopTable();
            prepTableExtract();

            DataTable DtConstants = dsBTM.Tables["Value"];
            dtFunctoids = dsBTM.Tables["Functoid"];
            dtParameters = dsBTM.Tables["Parameter"];
            string[] sep = new string[] { @"/*", "/@*", "/>" };

            //GET ALL CONSTANT VALUES
            if (DtConstants != null)
                foreach (DataRow dr in DtConstants.Rows)
                {
                    string[] t = dr["Query"].ToString().Replace("[local-name()='", "").Replace("']", "").Replace("Query=", "").Trim().Split(sep, StringSplitOptions.RemoveEmptyEntries);
                    string a = t[t.Length - 2];

                    fMap f = new fMap();
                    f.TargetTable = a;
                    f.TargetColumn = t[t.Length - 1].Replace("Label=", "").Trim();
                    f.targetPath = dr["Query"].ToString();
                    f.DefaultValue = dr["value"].ToString();
                    f.SourceTable = "HARDCODED VALUE";
                    f.Mapped = true;
                    FeildMaps.Add(f);
                }

            DataTable DtLinks = dsBTM.Tables["Link"];
            //GET ALL DIRECT LINKS
            foreach (DataRow dr in DtLinks.Rows)
            {
                if (dr["LinkFrom"].ToString().IndexOf("local-name()") != -1 && dr["LinkTo"].ToString().IndexOf("local-name()") != -1)
                {
                    string[] t = dr["LinkTo"].ToString().Replace("[local-name()='", "").Replace("']", "").Replace("Query=", "").Trim().Split(sep, StringSplitOptions.RemoveEmptyEntries);

                    string a = t[t.Length - 2];

                    fMap f = new fMap();
                    f.TargetTable = a;
                    f.TargetColumn = t[t.Length - 1].Replace("Label=", "").Trim();
                    f.targetPath = dr["LinkTo"].ToString();
                    f.LinkTo = dr["LinkTo"].ToString();
                    t = dr["LinkFrom"].ToString().Replace("[local-name()='", "").Replace("']", "").Replace("Query=", "").Trim().Split(sep, StringSplitOptions.RemoveEmptyEntries);
                    f.SourceTable = t[t.Length - 2]; ;
                    f.SourceColumn = t[t.Length - 1];
                    f.LinkFrom = dr["LinkFrom"].ToString();
                    f.LinkID = dr["LinkID"].ToString();
                    f.Mapped = true;
                    FeildMaps.Add(f);
                }
                //GET NUMBERED LINKS ONLY 
                else if (Regex.IsMatch(dr["LinkFrom"].ToString().Trim(), "^[0-9]*$") && Regex.IsMatch(dr["LinkTo"].ToString().Trim(), "^[0-9]*$"))
                {
                    fMap lnk = new fMap();
                    lnk.LinkFrom = dr["LinkFrom"].ToString().Trim();
                    lnk.LinkTo = dr["LinkTo"].ToString().Trim();
                    lnk.isTableLoop = IsConnectedtoTableLoop(lnk.LinkFrom);
                    lnk.LinkID = dr["LinkID"].ToString();
                    Links.Add(lnk);
                }
                //BELOW THIS TARGET COLUMNS
                else if (Regex.IsMatch(dr["LinkFrom"].ToString().Trim(), "^[0-9]*$"))
                {
                    string[] t = dr["LinkTo"].ToString().Replace("[local-name()='", "").Replace("']", "").Replace("Query=", "").Trim().Split(sep, StringSplitOptions.RemoveEmptyEntries);
                    string a = t[t.Length - 2];

                    fMap f = new fMap();
                    f.TargetTable = a;
                    f.TargetColumn = t[t.Length - 1].Replace("Label=", "").Trim();
                    f.targetPath = dr["LinkTo"].ToString();
                    f.LinkFrom = dr["LinkFrom"].ToString().Trim();
                    f.LinkID = dr["LinkID"].ToString();
                    LinkFrom.Add(f);
                }
                //GET SOURCE COLUMNS
                else if (Regex.IsMatch(dr["LinkTo"].ToString().Trim(), "^[0-9]*$"))
                {
                    string[] t = dr["LinkFrom"].ToString().Replace("[local-name()='", "").Replace("']", "").Replace("Query=", "").Trim().Split(sep, StringSplitOptions.RemoveEmptyEntries);
                    string a = t[t.Length - 2];

                    fMap f = new fMap();
                    f.SourceTable = a;
                    f.SourceColumn = t[t.Length - 1].Replace("Label=", "").Trim();
                    f.sourcePath = dr["LinkFrom"].ToString();
                    f.LinkTo = dr["LinkTo"].ToString().Trim();
                    f.LinkID = dr["LinkID"].ToString();
                    LinkTo.Add(f);
                }
            }

            ManipulateLinks();

            //START MATCHING WITH ONLY ONE INTERMEDIATE FUNTOifD
            foreach (fMap lnkf in LinkFrom)
            {
                bool MatchFound = false;
                foreach (fMap lnkto in LinkTo)
                {
                    if (lnkto.LinkTo == lnkf.LinkFrom)
                    {
                        lnkto.targetPath = lnkf.targetPath;
                        lnkto.TargetTable = lnkf.TargetTable;
                        lnkto.TargetColumn = lnkf.TargetColumn;
                        lnkto.Mapped = true;
                        lnkf.Mapped = true;
                        FeildMaps.Add(lnkto);
                        MatchFound = true;
                    }
                }

                //EVALUVATE LEFTOUT LINKS
                if (!MatchFound)
                {
                    if (IsCalculated(lnkf.LinkFrom))
                    {
                        lnkf.Mapped = true;
                        lnkf.SourceColumn = "CALCULATED FROM FUNCTOID";
                        lnkf.SourceTable = "CALCULATED VALUE";
                        FeildMaps.Add(lnkf);
                    }
                    else
                    {
                        // STORE NON MATCHED TARGET COLUMNS
                        MultiMatch.Add(lnkf);
                    }
                }
            }

            //TRY MATCHING UNMATCHED LINKS
            foreach (fMap nonMatch in MultiMatch)
            {
                Matchs = new List<fMap>();
                DoRecursiveCheck(nonMatch);
                foreach (fMap m in Matchs)
                {
                    m.TargetColumn = nonMatch.TargetColumn;
                    m.targetPath = nonMatch.targetPath;
                    m.TargetTable = nonMatch.TargetTable;
                    m.Mapped = true;
                    FeildMaps.Add(m);
                }
            }

            //FINALLY , AS WE HAVE REACHED HERE WITHOUT ANY ERRORS, WRITE IT TO CSV. :)
            StreamWriter reader = new StreamWriter(Path.GetFullPath(PATH) + ".csv");
            reader.AutoFlush = true;
            List<string> Unique = new List<string>();
            reader.WriteLine(@"SOURCE TABLE,SOURCE COLUNM\DEFAULT VALUE,TARGET COLUMN, TARGET TABLE,");
            foreach (fMap fm in FeildMaps)
            {
                string PrintValue = fm.SourceTable + "," + (fm.SourceColumn ?? fm.DefaultValue) + "," + fm.TargetColumn + "," + fm.TargetTable;
                if (!Unique.Contains(PrintValue))
                {
                    Unique.Add(PrintValue);
                    reader.WriteLine(PrintValue);
                }
            }
            reader.Close();
        }

        /// <summary>
        /// Here mapping is done between two links if they have multiple functoid between them
        /// </summary>
        /// <param name="tocheck"></param>
        private void DoRecursiveCheck(fMap tocheck)
        {
            foreach (fMap lnk in Links)
            {
                if (lnk.Mapped) continue;
                if (tocheck.LinkFrom == lnk.LinkTo)
                {
                    if (!lnk.isTableLoop) DoRecursiveCheck(lnk);
                    foreach (fMap lnkto in LinkTo)
                    {
                        if (lnk.LinkFrom == lnkto.LinkTo)
                        {
                            lnkto.Mapped = true;
                            lnk.Mapped = true;
                            Matchs.Add(lnkto);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Not being used as of now
        /// </summary>
        /// <param name="Map"></param>
        /// <returns></returns>
        private fMap MapFunctoid(fMap Map)
        {
            DataRow[] Dr = dsBTM.Tables["Functoid"].Select("FunctoidID='" + Map.LinkTo + "'");
            if (Dr.Length > 0)
            {
                Map.FunctoidName += Dr[0]["Label"].ToString().Trim() + ",";

            }

            return Map;
        }

        /// <summary>
        /// Evaluvates if any Numbered links point to table looping funtoid
        /// </summary>
        /// <param name="ID"></param>
        /// <returns></returns>
        private bool IsConnectedtoTableLoop(string ID)
        {
            if (dtFunctoids.Select("FunctoidID='" + ID + "' AND [Functoid-Name]='Table Looping'").Length > 0) return true;
            else return false;
        }

        /// <summary>
        /// Evaluvates if the target Column is evaluvated from functoid
        /// </summary>
        /// <param name="ID"></param>
        /// <returns></returns>
        private bool IsCalculated(string ID)
        {
            if (dtFunctoids.Select("FunctoidID='" + ID + "'").Length > 0)
            {
                foreach (fMap m in LinkTo)
                {
                    if (m.LinkTo == ID) return false;
                }
                foreach (fMap m in Links)
                {
                    if (m.LinkTo == ID) return false;
                }
            }
            return true;
        }
    }
}